package cn.samler.diandi.api.utils;

import cn.samler.diandi.api.config.JWTConfig;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;

@Component
@Slf4j
public class JWTUtil {
    private static JWTUtil ins;
    private final JWTConfig config;

    private JWTUtil(JWTConfig config) {
        this.config = config;
    }

    @PostConstruct
    private void init() {
        ins = new JWTUtil(this.config);
    }

    public static JWTUtil getInstance() {
        return ins;
    }

    public String sign(HashMap<String, Object> payload) {
        try {
            //过期时间
            Calendar calendar = Calendar.getInstance();
            calendar.add(Calendar.DATE, this.config.getExpireDay());

            //私钥及加密算法
            Algorithm algorithm = Algorithm.HMAC256(this.config.getSecretKey());

            //设置头信息
            HashMap<String, Object> header = new HashMap<>(2);
            header.put("typ", "JWT");
            header.put("alg", "HS256");

            JWTCreator.Builder jwt = JWT.create().withHeader(header);
            payload.forEach((k, v) -> {
                if (v instanceof Date)
                    jwt.withClaim(k, (Date) v);
                else if (v instanceof Long)
                    jwt.withClaim(k, (Long) v);
                else if (v instanceof Double)
                    jwt.withClaim(k, (Double) v);
                else if (v instanceof String)
                    jwt.withClaim(k, (String) v);
                else if (v instanceof Boolean)
                    jwt.withClaim(k, (Boolean) v);
                else if (v instanceof Integer)
                    jwt.withClaim(k, (Integer) v);
            });

            //附带username和userID生成签名
            return jwt.withExpiresAt(calendar.getTime()).sign(algorithm);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("JWT sign exception: " + e);
            return null;
        }
    }

    public DecodedJWT verify(String token) {
        try {
            Algorithm algorithm = Algorithm.HMAC256(this.config.getSecretKey());
            JWTVerifier verifier = JWT.require(algorithm).build();

            return verifier.verify(token);
        } catch (Exception e) {
            log.error("JWT verify exception: " + e);
            return null;
        }
    }

    public boolean isNeedRefresh(DecodedJWT jwt) {
        Date timeoutDate = jwt.getExpiresAt();
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DATE, this.config.getExpireDay() - this.config.getRefreshDay());

        return timeoutDate.before(calendar.getTime());
    }

    public boolean isAvailable(DecodedJWT jwt, String key, String reference) {
        return reference != null &&
                reference.equals(jwt.getClaim(key).asString());
    }

    public JSONObject getPayload(DecodedJWT jwt) {
        try {
            return JSON.parseObject(new String(Base64.getDecoder().decode(jwt.getPayload()), StandardCharsets.UTF_8));
        } catch (Exception e) {
            log.error("JWT get payload exception: " + e);
            return null;
        }
    }
}
